////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009 The Mist project                                        //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains strong- and weak-pointer classes, based on the smart pointers of
/// the Boost smart_ptr library, but with a more consistent interface.
///

#ifndef PTR_HPP
#define PTR_HPP

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::dynamic_pointer_cast;

#include <boost/weak_ptr.hpp>
using boost::weak_ptr;

#include <boost/enable_shared_from_this.hpp>
using boost::enable_shared_from_this;

#include <vector>
using std::vector;

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

#include "common/assert.hpp"

////////////////////////////////////////////////////////////////////////////////
// Forward Declarations                                                       //
////////////////////////////////////////////////////////////////////////////////

template <class T> class sptr;
template <class T> class wptr;

////////////////////////////////////////////////////////////////////////////////
// sptr Class                                                                 //
////////////////////////////////////////////////////////////////////////////////

/// A strong pointer, which 'owns' the object it points to, keeping it alive.
///
template <class T>
class sptr {
	public:
		
		/// The type of the element pointed to.
		///
		typedef T element_type;
		
		/// Construct a null strong pointer (pointing to nothing).
		///
		sptr() : ptr(shared_ptr<T>()) {}
		
		/// Construct a strong pointer pointing to the same object \a p
		/// points to. It takes ownership of the object.
		///
		template <class Y>
		explicit sptr(Y* p) : ptr(p) {}
		
		/// Construct a strong pointer pointing to the same object \a p
		/// points to. It will share ownership of the object.
		///
		template <class Y>
		sptr(const sptr<Y>& p) : ptr(p.ptr) {}
		
		/// Construct a strong pointer pointing to the same object \a p
		/// points to. It will share ownership of the object with any other
		/// strong pointers that are pointing to it.
		///
		template <class Y>
		sptr(const wptr<Y>& p) : ptr(p.ptr) {}
		
		/// Make this pointer point to the same object \a p points to.
		/// It will take ownership of the object.
		///
		template <class Y>
		sptr<T>& operator=(Y* p) {
			ptr = shared_ptr<Y>(p);
			return *this;
		}
		
		/// Make this pointer point to the same object \a p is pointing to.
		/// It will share ownership of the object.
		///
		template <class Y>
		sptr<T>& operator=(const sptr<Y>& p) {
			ptr = shared_ptr<Y>(p.ptr);
			return *this;
		}
		
		/// Make this pointer point to the same object \a p is pointing to.
		/// It will share ownership of the object with any other strong pointers
		/// that are pointing to it.
		///
		template <class Y>
		sptr<T>& operator=(const wptr<Y>& p) {
			ptr = shared_ptr<Y>(p.ptr);
			return *this;
		}
		
		/// \return false, if this pointer points to anything
		///         true , otherwise (it is a null pointer)
		///
		bool null() const {
			return ptr.get() == 0;
		}
		
		/// \return true , if this pointer points to anything
		///         false, otherwise (it is a null pointer)
		///
		operator bool() const {
			return !null();
		}
		
		/// \return false, if this pointer points to anything
		///         true , otherwise (it is a null pointer)
		///
		bool operator!() const {
			return null();
		}
		
		/// \return a reference to the object this pointer points to
		/// \pre !this.null()
		///
		T& operator*() const {
			assert(!null());
			
			return *ptr;
		}
		
		/// The member access operator.
		///
		/// \return a plain pointer to the object this pointer points to
		/// \pre !this.null()
		///
		T* operator->() const {
			assert(!null());
			
			return ptr.get();
		}
		
		/// Dynamic cast of this pointer to a compatible pointer type \a Y.
		///
		/// \return a strong pointer of type \a Y to the object this
		///         pointer points to
		///
		template <class Y>
		sptr<Y> cast() {
			sptr<Y> result;
			if (!null())
				result.ptr = dynamic_pointer_cast<Y>(ptr);
			return result;
		}
		
		shared_ptr<T> ptr;
};

template <class T>
bool operator<(sptr<T> a, sptr<T> b) {
	return (a.ptr < b.ptr);
}

/// \return a strong pointer to the object \a p points to
/// \relates sptr
///
template <class T>
sptr<T> n_sptr(T* p) {
	return sptr<T>(p);
}

/// \return a pointer to a new strong pointer to the object \a p points to
/// \relates sptr
///
template <class T>
sptr<T>* n_sptr_ptr(T* p) {
	return new sptr<T>(p);
}

/// \return a pointer to a new strong pointer to the object \a p points to
/// \relates sptr
///
template <class T>
sptr<T>* n_sptr_ptr(sptr<T> p) {
	return new sptr<T>(p);
}

/// A class to inherit if you want to be able to be able to get a strong pointer
/// to the object from a member-function, which shares ownership with other
/// existing strong pointers.
///
/// \relates sptr
///
template <class T>
class enable_ptr_this : public enable_shared_from_this<T> {
	public:
		
		/// \return a strong pointer to 'this', sharing ownership with other
		///         existing strong pointers
		///
		sptr<T> sptr_this() {
			sptr<T> result;
			result.ptr = enable_shared_from_this<T>::shared_from_this();
			return result;
		}
		
		/// \return a weak pointer to 'this', knowing about the ownership of
		///         existing strong pointers
		///
		wptr<T> wptr_this() {
			wptr<T> result;
			result.ptr = enable_shared_from_this<T>::shared_from_this();
			return result;
		}
};

////////////////////////////////////////////////////////////////////////////////
// wptr Class                                                                 //
////////////////////////////////////////////////////////////////////////////////

/// A weak pointer, which 'observes' the object it points to, but does not
/// keep it alive. If all strong pointers pointing to the object are gone,
/// all weak pointers to it will become null.
///
template <class T>
class wptr {
	public:
		
		/// The type of the element pointed to.
		///
		typedef T element_type;
		
		/// Construct a null weak pointer (pointing to nothing).
		///
		wptr() : ptr() {}
		
		/// Construct a weak pointer pointing to the same object \a p
		/// points to.
		///
		template <class Y>
		wptr(const sptr<Y>& p) : ptr(p.ptr) {}
		
		/// Construct a weak pointer pointing to the same object \a p
		/// points to.
		///
		template <class Y>
		wptr(const wptr<Y>& p) : ptr(p.ptr) {}
		
		/// Make this pointer point to the same object \a p points to.
		///
		template <class Y>
		wptr<T>& operator=(const sptr<Y>& p) {
			ptr = weak_ptr<Y>(p.ptr);
			return *this;
		}
		
		/// Make this pointer point to the same object \a p points to.
		///
		template <class Y>
		wptr<T>& operator=(const wptr<Y>& p) {
			ptr = weak_ptr<Y>(p.ptr);
			return *this;
		}
		
		/// \return false, if this pointer points to anything
		///         true , otherwise (it is a null pointer)
		///
		bool null() const {
			return ptr.expired();
		}
		
		/// \return true , if this pointer points to anything
		///         false, otherwise (it is a null pointer)
		///
		operator bool() const {
			return !null();
		}
		
		/// \return false, if this pointer points to anything
		///         true , otherwise (it is a null pointer)
		///
		bool operator!() const {
			return null();
		}
		
		/// \return a reference to the object this pointer points to
		/// \pre !this.null()
		///
		T& operator*() const {
			assert(!null());
			
			return *ptr.lock();
		}
		
		/// The member access operator.
		///
		/// \return a plain pointer to the object this pointer points to
		/// \pre !this.null()
		///
		T* operator->() const {
			assert(!null());
			
			return ptr.lock().get();
		}
		
		/// Dynamic cast of this pointer to a compatible pointer type \a Y.
		///
		/// \return a strong pointer of type \a Y to the object this
		///         pointer points to
		///
		template <class Y>
		wptr<Y> cast() {
			wptr<Y> result;
			if (!null())
				result.ptr = dynamic_pointer_cast<Y>(ptr.lock());
			return result;
		}
		
		weak_ptr<T> ptr;
};

template <class T>
bool operator<(wptr<T> a, wptr<T> b) {
	return (a.ptr < b.ptr);
}

template <class T>
vector<wptr<T> > weak_vector(const vector<sptr<T> >& v) {
	vector<wptr<T> > result;
	foreach (sptr<T> ptr, v)
		result.push_back(ptr);
	return result;
}

template <class T, class U>
bool operator==(sptr<T> a, sptr<U> b) {
	return (a.ptr == b.ptr);
}

template <class T, class U>
bool operator==(sptr<T> a, wptr<U> b) {
	return (a.ptr == b.ptr.lock());
}

template <class T, class U>
bool operator==(wptr<T> a, sptr<U> b) {
	return (a.ptr.lock() == b.ptr);
}

template <class T, class U>
bool operator==(wptr<T> a, wptr<U> b) {
	return (a.ptr.lock() == b.ptr.lock());
}

template <class T, class U>
bool operator!=(sptr<T> a, sptr<U> b) {
	return (a.ptr != b.ptr);
}

template <class T, class U>
bool operator!=(sptr<T> a, wptr<U> b) {
	return (a.ptr != b.ptr.lock());
}

template <class T, class U>
bool operator!=(wptr<T> a, sptr<U> b) {
	return (a.ptr.lock() != b.ptr);
}

template <class T, class U>
bool operator!=(wptr<T> a, wptr<U> b) {
	return (a.ptr.lock() != b.ptr.lock());
}

#endif // PTR_HPP
